import struct
import socket
import time
import numpy as np


# global variables
# TARGET_IP = '127.0.0.1'
# TARGET_PORT = 7777
TARGET_IP = '192.168.50.103'
TARGET_PORT = 6008

REDUNDANT_DATA_LEN = 1265
TOTAL_SUM = 10000 # total send sum

DELAY_MAX = 500    # max acceptable delay (in ms)

UDP_BUF_SIZE = 2048


seq_list = []
delays_list = np.zeros(TOTAL_SUM)
ber_list = np.zeros(TOTAL_SUM)

expected_data =  bytes([(i*255)//REDUNDANT_DATA_LEN for i in range(REDUNDANT_DATA_LEN)])
expected_bits = np.unpackbits(np.frombuffer(expected_data, dtype=np.uint8))

def parse_frame(frame,recv_time):
    try:
        seq,timestamp = struct.unpack("<IQ",frame[:12])
        recv_data = frame[12:]
        #calc frame recv sum
        if seq > TOTAL_SUM:
            print(f"err: seq{seq} is wrong")
            return
        if seq in seq_list:
            print(f"err: seq:{seq} repeated received")
        seq_list.append(seq)
        # calc delay
        delay = recv_time - timestamp
        if delay < 0:
            delay =0
            # print(f"err: seq:{seq} delay < 0")
        elif delay > DELAY_MAX:
            delay =0
            # print(f"err: seq:{seq} delay > {DELAY_MAX}")
        else:
            delays_list[seq] = delay
        # calc bit error
        recv_bits = np.unpackbits(np.frombuffer(recv_data, dtype=np.uint8))
        errors = np.sum(expected_bits != recv_bits)
        ber_list[seq] = errors
        
        if len(seq_list)%1 ==0:
            frame_cur_sum = len(set(seq_list))
            # calc delay
            delay_mask = (delays_list !=0)
            avg_delay = np.mean(delays_list,where=delay_mask)
            # calc ber
            cur_ber = np.sum(ber_list)/(frame_cur_sum*REDUNDANT_DATA_LEN*8)
            print(f"recv frame sum:{frame_cur_sum},current_seq:{seq},avg_delay:{avg_delay:.2f}ms,ber:{cur_ber:.6f}")
    except Exception as e:
        print(f"frame parse err:{e}!")
        
            
def gen_report():
    # calc frame Loss Rate
    received_count = len(set(seq_list))
    lost_frame = TOTAL_SUM - received_count
    loss_rate = lost_frame/TOTAL_SUM
    
    # calc average delay
    delay_mask = (delays_list !=0)
    avg_delay = np.mean(delays_list,where=delay_mask)
    
    #calc bits Loss Rate
    total_err_bits = np.sum(ber_list)
    ber = total_err_bits/(received_count*REDUNDANT_DATA_LEN*8)
    print("\n=========Recv Report=========\n")
    print(f"Send Sum:{TOTAL_SUM}")
    print(f"Received Sum:{received_count}")
    print(f"Frame Loss Rate:{loss_rate:.5e}")
    print(f"Ber:{ber:.8e}")
    print(f"Average Delay:{avg_delay:.2f}ms")
    

if __name__ == "__main__":
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind((TARGET_IP, TARGET_PORT))
    sock.setsockopt(socket.SOL_SOCKET, 25, b'enp46s0' + b'\0')
    # sock.setsockopt(socket.SOL_SOCKET, 25, b'enx00e04c6800ea' + b'\0')
    
    print(f"Listening on {TARGET_IP}:{TARGET_PORT}...")
    while(True):
        try:
            data,addr = sock.recvfrom(UDP_BUF_SIZE)
            
            # recv time(in ms)
            recv_time = time.time() * 1000
            parse_frame(data,recv_time)
            
        except KeyboardInterrupt:
            gen_report()
            break
